package nmea0183

import (
	"errors"
	"fmt"
	"strconv"
	"strings"

	"github.com/project-flogo/core/data/metadata"

	"github.com/project-flogo/core/activity"
)

type Activity struct {
	verify bool
}

func init() {
	_ = activity.Register(&Activity{}, New)
}

func New(ctx activity.InitContext) (activity.Activity, error) {
	var settings Settings
	err := metadata.MapToStruct(ctx.Settings(), settings, true)
	if err != nil {
		return nil, err
	}
	act := &Activity{
		verify: !settings.SkipVerify,
	}
	return act, nil
}

var activityMd = activity.ToMetadata(Input{}, Output{}, Settings{})

func (a *Activity) Metadata() *activity.Metadata {
	return activityMd
}

func (a *Activity) Eval(ctx activity.Context) (bool, error) {
	input := Input{}
	err := ctx.GetInputObject(&input)
	if err != nil {
		return true, err
	}
	if a.verify {
		if err := verifySentence(input.Sentence); err != nil {
			return true, err
		}
	}
	fields := splitSentence(input.Fields)
	fs := splitSentence(input.Sentence)
	if len(fs) != len(fields) {
		return true, fmt.Errorf("unmatch sentence: %s", input.Sentence)
	}
	data := map[string]string{}
	for idx, name := range fields {
		if name != "" {
			data[name] = fs[idx]
		}
	}

	output := Output{
		Data: data,
	}
	err = ctx.SetOutputObject(&output)
	return true, err
}

func verifySentence(str string) error {
	str = strings.TrimSpace(str)
	if len(str) < 4 {
		return errors.New("sentence too short")
	}
	if str[0] != '$' {
		return fmt.Errorf("sentence must start with $, got %v", str[0])
	}
	parts := strings.Split(str, "*")
	if len(parts) != 2 {
		return fmt.Errorf("sentence must contains 1 *")
	}
	if len(parts[1]) != 2 {
		return fmt.Errorf("sentence must have 1 byte checksum, got: %s", parts[1])
	}
	checksum := XorCheck([]byte(parts[0][1:]))
	checksumGot, err := toByte(parts[1])
	if err != nil {
		return fmt.Errorf("sentence checksum error: %v", err)
	}
	if checksum != checksumGot {
		return fmt.Errorf("sentence payload expect checksum to be %02X, got %02X", checksum, checksumGot)
	}
	return nil
}

func toByte(s string) (byte, error) {
	v, err := strconv.ParseUint(s, 16, 8)
	return byte(v), err
}

func splitSentence(str string) []string {
	fs := strings.Split(str, ",")
	if len(fs) > 0 {
		last := fs[len(fs)-1]
		part := strings.Split(last, "*")
		fs = append(fs[:len(fs)-1], part...)
	}
	return fs
}
